/*
 * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <linux/types.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/nodemask.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/ata.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/regulator/consumer.h>
#include <linux/pmic_external.h>
#include <linux/pmic_status.h>
#include <linux/ipu.h>
#include <linux/mxcfb.h>
#include <linux/pwm_backlight.h>
#include <linux/fec.h>

#include <mach/common.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <asm/mach/keypad.h>
#include <asm/mach/flash.h>
#include <mach/memory.h>
#include <mach/gpio.h>
#include <mach/mmc.h>
#include <mach/mxc_dvfs.h>
#include <mach/iomux-mx53.h>
#include <mach/i2c.h>
#include <mach/mxc_iim.h>
#include <mach/check_fuse.h>

#include "crm_regs.h"
#include "devices.h"
#include "usb.h"

/*!
 * @file mach-mx5/mx53_loco.c
 *
 * @brief This file contains MX53 loco board specific initialization routines.
 *
 * @ingroup MSL_MX53
 */

/* MX53 LOCO GPIO PIN configurations */

//-----------------------GPIO--------------------------------------------------

#define MX53_EXT_GPIO6 			(0*32 + 19)	/* GPIO1_19 */
#define MX53_EXT_GPIO1          (3*32 + 10)	/* GPIO_4_10 */
#define MX53_READY			    (3*32 + 11)	/* GPIO_4_11 */
#define MX53_MOVE_ACTIVE	    (3*32 + 12)	/* GPIO_4_12 */
#define MX53_CAPTURE	        (3*32 + 13)	/* GPIO_4_13 */
#define MX53_VIN1V8_EN	        (3*32 + 14)	/* GPIO_4_14 */
#define MX53_EXT_GPIO7          (3*32 + 15)	/* GPIO_4_15 */
#define MX53_USER2_LED_EN	    (0*32 + 13)	/* GPIO_1_13 */
#define MX53_USER1_LED_EN	    (0*32 + 14)	/* GPIO_1_14 */
#define MX53_RS_232_ON_OFF		(2*32 + 26)	/* GPIO_3_26 */
#define MX53_SD1_CD			    (2*32 + 27)	/* GPIO_3_27 */
#define MX53_PMIC_PWRGOOD		(2*32 + 30)	/* GPIO_3_30 */
#define MX53_PMIC_INT_B			(4*32 + 25)	/* GPIO_5_25 */
#define MX53_USER1_IMX_LED_EN	(2*32 + 19)	/* GPIO_3_19 */
#define MX53_USER2_IMX_LED_EN   (2*32 + 20)	/* GPIO_3_20 */
#define MX53_CSI0_RSTB		    (4*32 + 22)	/* GPIO_5_22 */
#define MX53_CSI0_MCLK			(4*32 + 23)	/* GPIO_5_23 */
#define MX53_CSIO_PWDN			(4*32 + 20)	/* GPIO_5_20 */










static iomux_v3_cfg_t mx53_loco_pads[] = {
	/* FEC */
#if 0
	MX53_PAD_FEC_MDC__FEC_MDC,
	MX53_PAD_FEC_MDIO__FEC_MDIO,
	MX53_PAD_FEC_REF_CLK__FEC_TX_CLK,
	MX53_PAD_FEC_RX_ER__FEC_RX_ER,
	MX53_PAD_FEC_CRS_DV__FEC_RX_DV,
	MX53_PAD_FEC_RXD1__FEC_RDATA_1,
	MX53_PAD_FEC_RXD0__FEC_RDATA_0,
	MX53_PAD_FEC_TX_EN__FEC_TX_EN,
	MX53_PAD_FEC_TXD1__FEC_TDATA_1,
	MX53_PAD_FEC_TXD0__FEC_TDATA_0,
#endif
	/*CAMERA CSI0 */

	// Conflict with UART1
	MX53_PAD_CSI0_DAT8__IPU_CSI0_D_8,
	MX53_PAD_CSI0_DAT9__IPU_CSI0_D_9,
		
	MX53_PAD_CSI0_DAT10__IPU_CSI0_D_10,
	MX53_PAD_CSI0_DAT11__IPU_CSI0_D_11,
//   end of confict
	MX53_PAD_CSI0_DAT12__IPU_CSI0_D_12,
	MX53_PAD_CSI0_DAT13__IPU_CSI0_D_13,
	MX53_PAD_CSI0_DAT14__IPU_CSI0_D_14,
	
	MX53_PAD_CSI0_DAT15__IPU_CSI0_D_15,
	MX53_PAD_CSI0_DAT16__IPU_CSI0_D_16,
	MX53_PAD_CSI0_DAT17__IPU_CSI0_D_17,
	MX53_PAD_CSI0_DAT18__IPU_CSI0_D_18,
	MX53_PAD_CSI0_DAT19__IPU_CSI0_D_19,
	
	MX53_PAD_CSI0_VSYNC__IPU_CSI0_VSYNC,
	MX53_PAD_CSI0_MCLK__IPU_CSI0_HSYNC,
	MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK,	
	MX53_PAD_CSI0_DAT4__GPIO5_22,     // MT9v139 Rest 
	
	

	/* I2C-1 */
	MX53_PAD_EIM_D21__I2C1_SCL, 	 
	MX53_PAD_EIM_D28__I2C1_SDA, 

	/* I2C-3 */
    MX53_PAD_GPIO_3__I2C3_SCL,       
    MX53_PAD_GPIO_16__I2C3_SDA,     
#if 1

	/* SPI -1 */
	 MX53_PAD_KEY_COL0__ECSPI1_SCLK,
	 MX53_PAD_KEY_ROW0__ECSPI1_MOSI,   
	 MX53_PAD_KEY_COL1__ECSPI1_MISO, 	  
	 MX53_PAD_KEY_ROW1__ECSPI1_SS0,	
//	 MX53_PAD_KEY_ROW1__GPIO4_9,
	
	 /* GPIO */

	 MX53_PAD_KEY_COL2__GPIO4_10,			  // EXT_GPIO1
	 MX53_PAD_KEY_ROW2__GPIO4_11,			  // READY
	 MX53_PAD_KEY_COL3__GPIO4_12,			  // MOVE_ACTIVE
	 MX53_PAD_KEY_ROW3__GPIO4_13,			  // CAPTURE
	 MX53_PAD_KEY_COL4__GPIO4_14,			  // VIN1V8_EN
	 MX53_PAD_KEY_ROW4__GPIO4_15,			  // EXT_GPIO7
	 MX53_PAD_SD2_DATA2__GPIO1_13, 		 	 // USER2_LED_EN
	 MX53_PAD_SD2_DATA1__GPIO1_14, 		  // USER1_LED_EN
	 MX53_PAD_EIM_D19__GPIO3_19,			  // USER1_IMX_LED_EN
	 MX53_PAD_EIM_D20__GPIO3_20,			  // USER2_IMX_LED_EN
	 MX53_PAD_EIM_D27__GPIO3_27,			  // SD1_CD
	 MX53_PAD_GPIO_19__GPIO4_5,			 	 // EXT_GPIO6 

	//PMIC
	// MX53_PAD_PMIC_STBY_REQ__PMIC_STBY_REQ	, //PMIC StandBy
	//MX53_PAD_EIM_D30__GPIO3_30, 			 // PMIC_PWRGOOD
	MX53_PAD_CSI0_DAT7__GPIO5_25,			 // PMIC_INT_B
#endif	 	
	
  };




static struct mxc_dvfs_platform_data dvfs_core_data = {
	.reg_id = "SW1",
	.clk1_id = "cpu_clk",
	.clk2_id = "gpc_dvfs_clk",
	.gpc_cntr_offset = MXC_GPC_CNTR_OFFSET,
	.gpc_vcr_offset = MXC_GPC_VCR_OFFSET,
	.ccm_cdcr_offset = MXC_CCM_CDCR_OFFSET,
	.ccm_cacrr_offset = MXC_CCM_CACRR_OFFSET,
	.ccm_cdhipr_offset = MXC_CCM_CDHIPR_OFFSET,
	.prediv_mask = 0x1F800,
	.prediv_offset = 11,
	.prediv_val = 3,
	.div3ck_mask = 0xE0000000,
	.div3ck_offset = 29,
	.div3ck_val = 2,
	.emac_val = 0x08,
	.upthr_val = 25,
	.dnthr_val = 9,
	.pncthr_val = 33,
	.upcnt_val = 10,
	.dncnt_val = 10,
	.delay_time = 30,
};


extern void mx5_ipu_reset(void);
static struct mxc_ipu_config mxc_ipu_data = {
	.rev = 3,
	.reset = mx5_ipu_reset,
};

extern void mx5_vpu_reset(void);
static struct mxc_vpu_platform_data mxc_vpu_data = {
	.iram_enable = true,
	.iram_size = 0x14000,
	.reset = mx5_vpu_reset,
};

static struct mxc_spi_master mxcspi1_data = {
	.maxchipselect = 4,
	.spi_version = 23,
};
static struct fec_platform_data fec_data = {
	.phy = PHY_INTERFACE_MODE_RMII,
};


static struct mxc_bus_freq_platform_data bus_freq_data;


static struct imxi2c_platform_data mxci2c1_data = {
	.bitrate = 100000,
};

static struct imxi2c_platform_data mxci2c3_data = {
	.bitrate = 400000,
};




static void mxc_iim_enable_fuse(void)
{
	u32 reg;

	if (!ccm_base)
		return;

	/* enable fuse blown */
	reg = readl(ccm_base + 0x64);
	reg |= 0x10;
	writel(reg, ccm_base + 0x64);
}

static void mxc_iim_disable_fuse(void)
{
	u32 reg;

	if (!ccm_base)
		return;
	/* enable fuse blown */
	reg = readl(ccm_base + 0x64);
	reg &= ~0x10;
	writel(reg, ccm_base + 0x64);
}

static struct mxc_iim_data iim_data = {
	.bank_start = MXC_IIM_MX53_BANK_START_ADDR,
	.bank_end   = MXC_IIM_MX53_BANK_END_ADDR,
	.enable_fuse = mxc_iim_enable_fuse,
	.disable_fuse = mxc_iim_disable_fuse,
};



static struct imxi2c_platform_data mxci2c_data = {
       .bitrate = 100000,
};


static struct mxc_bus_freq_platform_data bus_freq_data = {
	.gp_reg_id = "SW1",
	.lp_reg_id = "SW2",
};



static void mt9v139_camera_pwdn(int pwdn)
{

}


static struct mxc_camera_platform_data mt9v139_camera_data = {
	.core_regulator = "LDO3",
	.mclk = 27000000,
	.csi = 0,
	.pwdn = mt9v139_camera_pwdn,
};



static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
	{
	 .type = "mt9v139",
	 .addr = 0x48,
	 .platform_data = (void *)&mt9v139_camera_data,
	 },
};

static struct mxc_asrc_platform_data mxc_asrc_data = {
	.channel_bits = 4,
	.clk_map_ver = 2,
};



static unsigned int sdhc_get_card_det_status(struct device *dev)
{
	int ret = 0;
	//return 0;
	if (to_platform_device(dev)->id == 0)
		ret = gpio_get_value(MX53_SD1_CD);
	
	return ret;
}


static struct mxc_mmc_platform_data mmc1_data = {
	.ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30
		| MMC_VDD_31_32,
	.caps = MMC_CAP_4_BIT_DATA,
	.min_clk = 400000,
	.max_clk = 50000000,
	.card_inserted_state = 0,
	.status = sdhc_get_card_det_status,
	.clock_mmc = "esdhc_clk",
	.power_mmc = NULL,
};


static struct spi_board_info __initdata emza_spi_device = {
	.modalias = "emza_spi",
	.irq = 101,
	.max_speed_hz = 6000000,	/* max spi SCK clock speed in HZ */
	.bus_num = 1,
	.chip_select = 0,
	.platform_data = 0,
};



/*!
 * Board specific fixup function. It is called by \b setup_arch() in
 * setup.c file very early on during kernel starts. It allows the user to
 * statically fill in the proper values for the passed-in parameters. None of
 * the parameters is used currently.
 *
 * @param  desc         pointer to \b struct \b machine_desc
 * @param  tags         pointer to \b struct \b tag
 * @param  cmdline      pointer to the command line
 * @param  mi           pointer to \b struct \b meminfo
 */
static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
				   char **cmdline, struct meminfo *mi)
{
	struct tag *t;
	struct tag *mem_tag = 0;
	int total_mem = SZ_1G;
	int left_mem = 0;
	int gpu_mem = SZ_128M;
	int fb_mem = SZ_32M;
	char *str;

	mxc_set_cpu_type(MXC_CPU_MX53);
	

	for_each_tag(mem_tag, tags) {
		if (mem_tag->hdr.tag == ATAG_MEM) {
			total_mem = mem_tag->u.mem.size;
			break;
		}
	}

	for_each_tag(t, tags) {
		if (t->hdr.tag == ATAG_CMDLINE) {
			str = t->u.cmdline.cmdline;
			str = strstr(str, "mem=");
			if (str != NULL) {
				str += 4;
				left_mem = memparse(str, &str);
			}

			str = t->u.cmdline.cmdline;
			str = strstr(str, "gpu_nommu");
			if (str != NULL)
				gpu_data.enable_mmu = 0;

			str = t->u.cmdline.cmdline;
			str = strstr(str, "gpu_memory=");
			if (str != NULL) {
				str += 11;
				gpu_mem = memparse(str, &str);
			}

			break;
		}
	}

	if (gpu_data.enable_mmu)
		gpu_mem = 0;

	if (left_mem == 0 || left_mem > total_mem)
		left_mem = total_mem - gpu_mem - fb_mem;

	if (mem_tag) {
		fb_mem = total_mem - left_mem - gpu_mem;
		if (fb_mem < 0) {
			gpu_mem = total_mem - left_mem;
			fb_mem = 0;
		}
		mem_tag->u.mem.size = left_mem;

		/*reserve memory for gpu*/
		if (!gpu_data.enable_mmu) {
			gpu_device.resource[5].start =
				mem_tag->u.mem.start + left_mem;
			gpu_device.resource[5].end =
				gpu_device.resource[5].start + gpu_mem - 1;
		}

	}
}

static void __init mx53_loco_io_init(void)
{

	mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads,
					ARRAY_SIZE(mx53_loco_pads));

	/* SD1 CD */
#if 1	
	gpio_request(MX53_SD1_CD, "sd1-cd");
	gpio_direction_input(MX53_SD1_CD);
	/*UART 1 On/Off */
	//gpio_request(MX53_RS_232_ON_OFF,"uart1-on");
	//gpio_direction_output(MX53_SD1_CD,1);
#endif


	//Camera Reset
	gpio_request(MX53_CSI0_RSTB,"csi0 reset");
	gpio_direction_output(MX53_CSI0_RSTB,0);
	msleep(10);
	gpio_direction_output(MX53_CSI0_RSTB,1);


// PMIC
	gpio_request(MX53_PMIC_INT_B, "pmic-int");
	gpio_direction_input(MX53_PMIC_INT_B); /*PMIC_INT*/
	//gpio_request(ARD_PMIC_RDY, "pmic-rdy");
	//gpio_direction_input(ARD_PMIC_RDY); /*PMIC_RDY*/
//	gpio_request(ARD_PMIC_PBSTAT, "pmic-pbstat");
//	gpio_direction_input(ARD_PMIC_PBSTAT);	/*PMIC_PBSTAT*/
	// spi to gpio
	//GPIO4_9
//	gpio_request((3*32 + 9), "spi-cs");
	// 

	gpio_request(MX53_EXT_GPIO7,"pnina");
	gpio_direction_output(MX53_EXT_GPIO7,0);
}

/*!
 * Board specific initialization.
 */
static void __init mxc_board_init(void)
{

//printk(KERN_INFO "%s.%d cspi_clk(%x)\n\r",__func__,__LINE__,clk_get(&mxcspi1_device,"cspi_clk"));

	mxc_ipu_data.di_clk[0] = clk_get(NULL, "ipu_di0_clk");
	mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di1_clk");
	mxc_ipu_data.csi_clk[0] = clk_get(NULL, "ssi_ext1_clk");

	mxcsdhc1_device.resource[2].start = gpio_to_irq(MX53_SD1_CD);
	mxcsdhc1_device.resource[2].end = gpio_to_irq(MX53_SD1_CD);


	mxc_cpu_common_init();
	mx53_loco_io_init();

	mxc_register_device(&mxc_dma_device, NULL);
	mxc_register_device(&mxc_wdt_device, NULL);
	mxc_register_device(&mxcspi1_device, &mxcspi1_data);
	mxc_register_device(&mxci2c_devices[0], &mxci2c1_data);
	mxc_register_device(&mxci2c_devices[2], &mxci2c1_data);


//	mxc_register_device(&mxc_rtc_device, NULL);
	mxc_register_device(&mxc_ipu_device, &mxc_ipu_data);


	spi_register_board_info(&emza_spi_device, 1);

	
	mxc_register_device(&mxc_dvfs_core_device, &dvfs_core_data);
	mxc_register_device(&busfreq_device, &bus_freq_data);

	if (!mxc_fuse_get_vpu_status())
		mxc_register_device(&mxcvpu_device, &mxc_vpu_data);
	//if (!mxc_fuse_get_gpu_status())
	//	mxc_register_device(&gpu_device, &gpu_data);
//	mxc_register_device(&mxcscc_device, NULL);
//	mxc_register_device(&busfreq_device, &bus_freq_data);
//	mxc_register_device(&mxc_iim_device, &iim_data);
	mxc_register_device(&mxcsdhc1_device, &mmc1_data);
	mxc_register_device(&mxc_fec_device, &fec_data);
//	mxc_register_device(&mxc_ptp_device, NULL);
	/* ASRC is only available for MX53 TO2.0 */
	if (mx53_revision() >= IMX_CHIP_REVISION_2_0) {
		//mxc_asrc_data.asrc_core_clk = clk_get(NULL, "asrc_clk");
		//clk_put(mxc_asrc_data.asrc_core_clk);
		//mxc_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk");
		//clk_put(mxc_asrc_data.asrc_audio_clk);
		//mxc_register_device(&mxc_asrc_device, &mxc_asrc_data);
	}

	i2c_register_board_info(2, mxc_i2c2_board_info,ARRAY_SIZE(mxc_i2c2_board_info));
	printk(KERN_INFO "%s.%d regsiter camera\n\r",__func__,__LINE__);


	//mx5_usb_dr_init();
	//mx5_set_host1_vbus_func(mx53_loco_usbh1_vbus);
	//mx5_usbh1_init();
	mxc_register_device(&mxc_v4l2_device, NULL);
	//mxc_register_device(&mxc_v4l2out_device, NULL);
	
}

static void __init mx53_loco_timer_init(void)
{
	struct clk *uart_clk;

	mx53_clocks_init(32768, 24000000, 0, 0);

	uart_clk = clk_get_sys("mxcintuart.0", NULL);
	early_console_setup(MX53_BASE_ADDR(UART3_BASE_ADDR), uart_clk);
}

static struct sys_timer mxc_timer = {
	.init	= mx53_loco_timer_init,
};

/*
 * The following uses standard kernel macros define in arch.h in order to
 * initialize __mach_desc_MX53_LOCO data structure.
 */
MACHINE_START(MX53_LOCO, "Freescale MX53 LOCO Board")
	/* Maintainer: Freescale Semiconductor, Inc. */
	.fixup = fixup_mxc_board,
	.map_io = mx5_map_io,
	.init_irq = mx5_init_irq,
	.init_machine = mxc_board_init,
	.timer = &mxc_timer,
MACHINE_END
